/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- *//* vim: set ts=8 sts=4 et sw=4 tw=80: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"mozilla/ArrayUtils.h"#include"mozilla/Attributes.h"#include"mozilla/Assertions.h"#include"mozilla/DebugOnly.h"#include"necko-config.h"#include"nsCache.h"#include"nsCacheService.h"#include"nsCacheRequest.h"#include"nsCacheEntry.h"#include"nsCacheEntryDescriptor.h"#include"nsCacheDevice.h"#include"nsMemoryCacheDevice.h"#include"nsICacheVisitor.h"#include"nsDiskCacheDevice.h"#include"nsDiskCacheDeviceSQL.h"#include"nsCacheUtils.h"#include"../cache2/CacheObserver.h"#include"nsIObserverService.h"#include"nsIPrefService.h"#include"nsIPrefBranch.h"#include"nsIFile.h"#include"nsIOService.h"#include"nsDirectoryServiceDefs.h"#include"nsAppDirectoryServiceDefs.h"#include"nsThreadUtils.h"#include"nsProxyRelease.h"#include"nsDeleteDir.h"#include"nsNetCID.h"#include<math.h> // for log()#include"mozilla/Services.h"#include"nsITimer.h"#include"mozIStorageService.h"#include"mozilla/net/NeckoCommon.h"#include<algorithm>usingnamespacemozilla;usingnamespacemozilla::net;/****************************************************************************** * nsCacheProfilePrefObserver *****************************************************************************/#define DISK_CACHE_ENABLE_PREF "browser.cache.disk.enable"#define DISK_CACHE_DIR_PREF "browser.cache.disk.parent_directory"#define DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF\ "browser.cache.disk.smart_size.first_run"#define DISK_CACHE_SMART_SIZE_ENABLED_PREF \ "browser.cache.disk.smart_size.enabled"#define DISK_CACHE_SMART_SIZE_PREF "browser.cache.disk.smart_size_cached_value"#define DISK_CACHE_CAPACITY_PREF "browser.cache.disk.capacity"#define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size"#define DISK_CACHE_CAPACITY 256000#define DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF \ "browser.cache.disk.smart_size.use_old_max"#define OFFLINE_CACHE_ENABLE_PREF "browser.cache.offline.enable"#define OFFLINE_CACHE_DIR_PREF "browser.cache.offline.parent_directory"#define OFFLINE_CACHE_CAPACITY_PREF "browser.cache.offline.capacity"#define OFFLINE_CACHE_CAPACITY 512000#define MEMORY_CACHE_ENABLE_PREF "browser.cache.memory.enable"#define MEMORY_CACHE_CAPACITY_PREF "browser.cache.memory.capacity"#define MEMORY_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.memory.max_entry_size"#define CACHE_COMPRESSION_LEVEL_PREF "browser.cache.compression_level"#define CACHE_COMPRESSION_LEVEL 1#define SANITIZE_ON_SHUTDOWN_PREF "privacy.sanitize.sanitizeOnShutdown"#define CLEAR_ON_SHUTDOWN_PREF "privacy.clearOnShutdown.cache"staticconstchar*observerList[]={"profile-before-change","profile-do-change",NS_XPCOM_SHUTDOWN_OBSERVER_ID,"last-pb-context-exited","suspend_process_notification","resume_process_notification"};staticconstchar*prefList[]={DISK_CACHE_ENABLE_PREF,DISK_CACHE_SMART_SIZE_ENABLED_PREF,DISK_CACHE_CAPACITY_PREF,DISK_CACHE_DIR_PREF,DISK_CACHE_MAX_ENTRY_SIZE_PREF,DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,OFFLINE_CACHE_ENABLE_PREF,OFFLINE_CACHE_CAPACITY_PREF,OFFLINE_CACHE_DIR_PREF,MEMORY_CACHE_ENABLE_PREF,MEMORY_CACHE_CAPACITY_PREF,MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,CACHE_COMPRESSION_LEVEL_PREF,SANITIZE_ON_SHUTDOWN_PREF,CLEAR_ON_SHUTDOWN_PREF};// Cache sizes, in KBconstint32_tDEFAULT_CACHE_SIZE=250*1024;// 250 MB#ifdef ANDROIDconstint32_tMAX_CACHE_SIZE=200*1024;// 200 MBconstint32_tOLD_MAX_CACHE_SIZE=200*1024;// 200 MB#elseconstint32_tMAX_CACHE_SIZE=350*1024;// 350 MBconstint32_tOLD_MAX_CACHE_SIZE=1024*1024;// 1 GB#endif// Default cache size was 50 MB for many years until FF 4:constint32_tPRE_GECKO_2_0_DEFAULT_CACHE_SIZE=50*1024;classnsCacheProfilePrefObserver:publicnsIObserver{virtual~nsCacheProfilePrefObserver(){}public:NS_DECL_THREADSAFE_ISUPPORTSNS_DECL_NSIOBSERVERnsCacheProfilePrefObserver():mHaveProfile(false),mDiskCacheEnabled(false),mDiskCacheCapacity(0),mDiskCacheMaxEntrySize(-1)// -1 means "no limit",mSmartSizeEnabled(false),mShouldUseOldMaxSmartSize(false),mOfflineCacheEnabled(false),mOfflineCacheCapacity(0),mMemoryCacheEnabled(true),mMemoryCacheCapacity(-1),mMemoryCacheMaxEntrySize(-1)// -1 means "no limit",mCacheCompressionLevel(CACHE_COMPRESSION_LEVEL),mSanitizeOnShutdown(false),mClearCacheOnShutdown(false){}nsresultInstall();voidRemove();nsresultReadPrefs(nsIPrefBranch*branch);boolDiskCacheEnabled();int32_tDiskCacheCapacity(){returnmDiskCacheCapacity;}voidSetDiskCacheCapacity(int32_t);int32_tDiskCacheMaxEntrySize(){returnmDiskCacheMaxEntrySize;}nsIFile*DiskCacheParentDirectory(){returnmDiskCacheParentDirectory;}boolSmartSizeEnabled(){returnmSmartSizeEnabled;}boolShouldUseOldMaxSmartSize(){returnmShouldUseOldMaxSmartSize;}voidSetUseNewMaxSmartSize(booluseNew){mShouldUseOldMaxSmartSize=!useNew;}boolOfflineCacheEnabled();int32_tOfflineCacheCapacity(){returnmOfflineCacheCapacity;}nsIFile*OfflineCacheParentDirectory(){returnmOfflineCacheParentDirectory;}boolMemoryCacheEnabled();int32_tMemoryCacheCapacity();int32_tMemoryCacheMaxEntrySize(){returnmMemoryCacheMaxEntrySize;}int32_tCacheCompressionLevel();boolSanitizeAtShutdown(){returnmSanitizeOnShutdown&&mClearCacheOnShutdown;}staticuint32_tGetSmartCacheSize(constnsAString&cachePath,uint32_tcurrentSize,boolshouldUseOldMaxSmartSize);boolPermittedToSmartSize(nsIPrefBranch*,boolfirstRun);private:boolmHaveProfile;boolmDiskCacheEnabled;int32_tmDiskCacheCapacity;// in kilobytesint32_tmDiskCacheMaxEntrySize;// in kilobytesnsCOMPtr<nsIFile>mDiskCacheParentDirectory;boolmSmartSizeEnabled;boolmShouldUseOldMaxSmartSize;boolmOfflineCacheEnabled;int32_tmOfflineCacheCapacity;// in kilobytesnsCOMPtr<nsIFile>mOfflineCacheParentDirectory;boolmMemoryCacheEnabled;int32_tmMemoryCacheCapacity;// in kilobytesint32_tmMemoryCacheMaxEntrySize;// in kilobytesint32_tmCacheCompressionLevel;boolmSanitizeOnShutdown;boolmClearCacheOnShutdown;};NS_IMPL_ISUPPORTS(nsCacheProfilePrefObserver,nsIObserver)classnsSetDiskSmartSizeCallbackfinal:publicnsITimerCallback{~nsSetDiskSmartSizeCallback(){}public:NS_DECL_THREADSAFE_ISUPPORTSNS_IMETHODNotify(nsITimer*aTimer)override{if(nsCacheService::gService){nsCacheServiceAutoLockautoLock(LOCK_TELEM(NSSETDISKSMARTSIZECALLBACK_NOTIFY));nsCacheService::gService->SetDiskSmartSize_Locked();nsCacheService::gService->mSmartSizeTimer=nullptr;}returnNS_OK;}};NS_IMPL_ISUPPORTS(nsSetDiskSmartSizeCallback,nsITimerCallback)// Runnable sent to main thread after the cache IO thread calculates available// disk space, so that there is no race in setting mDiskCacheCapacity.classnsSetSmartSizeEvent:publicRunnable{public:explicitnsSetSmartSizeEvent(int32_tsmartSize):mozilla::Runnable("nsSetSmartSizeEvent"),mSmartSize(smartSize){}NS_IMETHODRun(){NS_ASSERTION(NS_IsMainThread(),"Setting smart size data off the main thread");// Main thread may have already called nsCacheService::Shutdownif(!nsCacheService::IsInitialized())returnNS_ERROR_NOT_AVAILABLE;// Ensure smart sizing wasn't switched off while event was pending.// It is safe to access the observer without the lock since we are// on the main thread and the value changes only on the main thread.if(!nsCacheService::gService->mObserver->SmartSizeEnabled())returnNS_OK;nsCacheService::SetDiskCacheCapacity(mSmartSize);nsCOMPtr<nsIPrefBranch>ps=do_GetService(NS_PREFSERVICE_CONTRACTID);if(!ps||NS_FAILED(ps->SetIntPref(DISK_CACHE_SMART_SIZE_PREF,mSmartSize)))NS_WARNING("Failed to set smart size pref");returnNS_OK;}private:int32_tmSmartSize;};// Runnable sent from main thread to cacheIO threadclassnsGetSmartSizeEvent:publicRunnable{public:nsGetSmartSizeEvent(constnsAString&cachePath,uint32_tcurrentSize,boolshouldUseOldMaxSmartSize):mozilla::Runnable("nsGetSmartSizeEvent"),mCachePath(cachePath),mCurrentSize(currentSize),mShouldUseOldMaxSmartSize(shouldUseOldMaxSmartSize){}// Calculates user's disk space available on a background thread and// dispatches this value back to the main thread.NS_IMETHODRun()override{uint32_tsize;size=nsCacheProfilePrefObserver::GetSmartCacheSize(mCachePath,mCurrentSize,mShouldUseOldMaxSmartSize);NS_DispatchToMainThread(newnsSetSmartSizeEvent(size));returnNS_OK;}private:nsStringmCachePath;uint32_tmCurrentSize;boolmShouldUseOldMaxSmartSize;};classnsBlockOnCacheThreadEvent:publicRunnable{public:nsBlockOnCacheThreadEvent():mozilla::Runnable("nsBlockOnCacheThreadEvent"){}NS_IMETHODRun()override{nsCacheServiceAutoLockautoLock(LOCK_TELEM(NSBLOCKONCACHETHREADEVENT_RUN));CACHE_LOG_DEBUG(("nsBlockOnCacheThreadEvent [%p]\n",this));nsCacheService::gService->mNotified=true;nsCacheService::gService->mCondVar.Notify();returnNS_OK;}};nsresultnsCacheProfilePrefObserver::Install(){// install profile-change observernsCOMPtr<nsIObserverService>observerService=mozilla::services::GetObserverService();if(!observerService)returnNS_ERROR_FAILURE;nsresultrv,rv2=NS_OK;for(unsignedinti=0;i<ArrayLength(observerList);i++){rv=observerService->AddObserver(this,observerList[i],false);if(NS_FAILED(rv))rv2=rv;}// install preferences observernsCOMPtr<nsIPrefBranch>branch=do_GetService(NS_PREFSERVICE_CONTRACTID);if(!branch)returnNS_ERROR_FAILURE;for(unsignedinti=0;i<ArrayLength(prefList);i++){rv=branch->AddObserver(prefList[i],this,false);if(NS_FAILED(rv))rv2=rv;}// Determine if we have a profile already// Install() is called *after* the profile-after-change notification// when there is only a single profile, or it is specified on the// commandline at startup.// In that case, we detect the presence of a profile by the existence// of the NS_APP_USER_PROFILE_50_DIR directory.nsCOMPtr<nsIFile>directory;rv=NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,getter_AddRefs(directory));if(NS_SUCCEEDED(rv))mHaveProfile=true;rv=ReadPrefs(branch);NS_ENSURE_SUCCESS(rv,rv);returnrv2;}voidnsCacheProfilePrefObserver::Remove(){// remove Observer Service observersnsCOMPtr<nsIObserverService>obs=mozilla::services::GetObserverService();if(obs){for(unsignedinti=0;i<ArrayLength(observerList);i++){obs->RemoveObserver(this,observerList[i]);}}// remove Pref Service observersnsCOMPtr<nsIPrefBranch>prefs=do_GetService(NS_PREFSERVICE_CONTRACTID);if(!prefs)return;for(unsignedinti=0;i<ArrayLength(prefList);i++)prefs->RemoveObserver(prefList[i],this);// remove cache pref observers}voidnsCacheProfilePrefObserver::SetDiskCacheCapacity(int32_tcapacity){mDiskCacheCapacity=std::max(0,capacity);}NS_IMETHODIMPnsCacheProfilePrefObserver::Observe(nsISupports*subject,constchar*topic,constchar16_t*data_unicode){nsresultrv;NS_ConvertUTF16toUTF8data(data_unicode);CACHE_LOG_INFO(("Observe [topic=%s data=%s]\n",topic,data.get()));if(!nsCacheService::IsInitialized()){if(!strcmp("resume_process_notification",topic)){// A suspended process has a closed cache, so re-open it here.nsCacheService::GlobalInstance()->Init();}returnNS_OK;}if(!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID,topic)){// xpcom going away, shutdown cache servicensCacheService::GlobalInstance()->Shutdown();}elseif(!strcmp("profile-before-change",topic)){// profile before changemHaveProfile=false;// XXX shutdown devicesnsCacheService::OnProfileShutdown();}elseif(!strcmp("suspend_process_notification",topic)){// A suspended process may never return, so shutdown the cache to reduce// cache corruption.nsCacheService::GlobalInstance()->Shutdown();}elseif(!strcmp("profile-do-change",topic)){// profile after changemHaveProfile=true;nsCOMPtr<nsIPrefBranch>branch=do_GetService(NS_PREFSERVICE_CONTRACTID);if(!branch){returnNS_ERROR_FAILURE;}(void)ReadPrefs(branch);nsCacheService::OnProfileChanged();}elseif(!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID,topic)){// ignore pref changes until we're done switch profilesif(!mHaveProfile)returnNS_OK;nsCOMPtr<nsIPrefBranch>branch=do_QueryInterface(subject,&rv);if(NS_FAILED(rv))returnrv;// which preference changed?if(!strcmp(DISK_CACHE_ENABLE_PREF,data.get())){rv=branch->GetBoolPref(DISK_CACHE_ENABLE_PREF,&mDiskCacheEnabled);if(NS_FAILED(rv))returnrv;nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());}elseif(!strcmp(DISK_CACHE_CAPACITY_PREF,data.get())){int32_tcapacity=0;rv=branch->GetIntPref(DISK_CACHE_CAPACITY_PREF,&capacity);if(NS_FAILED(rv))returnrv;mDiskCacheCapacity=std::max(0,capacity);nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);// Update the cache capacity when smart sizing is turned on/off }elseif(!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF,data.get())){// Is the update because smartsizing was turned on, or off?rv=branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,&mSmartSizeEnabled);if(NS_FAILED(rv))returnrv;int32_tnewCapacity=0;if(mSmartSizeEnabled){nsCacheService::SetDiskSmartSize();}else{// Smart sizing switched off: use user specified sizerv=branch->GetIntPref(DISK_CACHE_CAPACITY_PREF,&newCapacity);if(NS_FAILED(rv))returnrv;mDiskCacheCapacity=std::max(0,newCapacity);nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);}}elseif(!strcmp(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,data.get())){rv=branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,&mShouldUseOldMaxSmartSize);if(NS_FAILED(rv))returnrv;}elseif(!strcmp(DISK_CACHE_MAX_ENTRY_SIZE_PREF,data.get())){int32_tnewMaxSize;rv=branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,&newMaxSize);if(NS_FAILED(rv))returnrv;mDiskCacheMaxEntrySize=std::max(-1,newMaxSize);nsCacheService::SetDiskCacheMaxEntrySize(mDiskCacheMaxEntrySize);#if 0 } else if (!strcmp(DISK_CACHE_DIR_PREF, data.get())) { // XXX We probaby don't want to respond to this pref except after // XXX profile changes. Ideally, there should be somekind of user // XXX notification that the pref change won't take effect until // XXX the next time the profile changes (browser launch)#endif }else// which preference changed?if(!strcmp(OFFLINE_CACHE_ENABLE_PREF,data.get())){rv=branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,&mOfflineCacheEnabled);if(NS_FAILED(rv))returnrv;nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());}elseif(!strcmp(OFFLINE_CACHE_CAPACITY_PREF,data.get())){int32_tcapacity=0;rv=branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF,&capacity);if(NS_FAILED(rv))returnrv;mOfflineCacheCapacity=std::max(0,capacity);nsCacheService::SetOfflineCacheCapacity(mOfflineCacheCapacity);#if 0 } else if (!strcmp(OFFLINE_CACHE_DIR_PREF, data.get())) { // XXX We probaby don't want to respond to this pref except after // XXX profile changes. Ideally, there should be some kind of user // XXX notification that the pref change won't take effect until // XXX the next time the profile changes (browser launch)#endif}elseif(!strcmp(MEMORY_CACHE_ENABLE_PREF,data.get())){rv=branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF,&mMemoryCacheEnabled);if(NS_FAILED(rv))returnrv;nsCacheService::SetMemoryCache();}elseif(!strcmp(MEMORY_CACHE_CAPACITY_PREF,data.get())){mMemoryCacheCapacity=-1;(void)branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,&mMemoryCacheCapacity);nsCacheService::SetMemoryCache();}elseif(!strcmp(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,data.get())){int32_tnewMaxSize;rv=branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,&newMaxSize);if(NS_FAILED(rv))returnrv;mMemoryCacheMaxEntrySize=std::max(-1,newMaxSize);nsCacheService::SetMemoryCacheMaxEntrySize(mMemoryCacheMaxEntrySize);}elseif(!strcmp(CACHE_COMPRESSION_LEVEL_PREF,data.get())){mCacheCompressionLevel=CACHE_COMPRESSION_LEVEL;(void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,&mCacheCompressionLevel);mCacheCompressionLevel=std::max(0,mCacheCompressionLevel);mCacheCompressionLevel=std::min(9,mCacheCompressionLevel);}elseif(!strcmp(SANITIZE_ON_SHUTDOWN_PREF,data.get())){rv=branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,&mSanitizeOnShutdown);if(NS_FAILED(rv))returnrv;nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());}elseif(!strcmp(CLEAR_ON_SHUTDOWN_PREF,data.get())){rv=branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,&mClearCacheOnShutdown);if(NS_FAILED(rv))returnrv;nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());}}elseif(!strcmp("last-pb-context-exited",topic)){nsCacheService::LeavePrivateBrowsing();}returnNS_OK;}// Returns default ("smart") size (in KB) of cache, given available disk space// (also in KB)staticuint32_tSmartCacheSize(constuint32_tavailKB,boolshouldUseOldMaxSmartSize){uint32_tmaxSize=shouldUseOldMaxSmartSize?OLD_MAX_CACHE_SIZE:MAX_CACHE_SIZE;if(availKB>100*1024*1024)returnmaxSize;// skip computing if we're over 100 GB// Grow/shrink in 10 MB units, deliberately, so that in the common case we// don't shrink cache and evict items every time we startup (it's important// that we don't slow down startup benchmarks).uint32_tsz10MBs=0;uint32_tavail10MBs=availKB/(1024*10);// .5% of space above 25 GBif(avail10MBs>2500){sz10MBs+=static_cast<uint32_t>((avail10MBs-2500)*.005);avail10MBs=2500;}// 1% of space between 7GB -> 25 GBif(avail10MBs>700){sz10MBs+=static_cast<uint32_t>((avail10MBs-700)*.01);avail10MBs=700;}// 5% of space between 500 MB -> 7 GBif(avail10MBs>50){sz10MBs+=static_cast<uint32_t>((avail10MBs-50)*.05);avail10MBs=50;}#ifdef ANDROID// On Android, smaller/older devices may have very little storage and// device owners may be sensitive to storage footprint: Use a smaller// percentage of available space and a smaller minimum.// 20% of space up to 500 MB (10 MB min)sz10MBs+=std::max<uint32_t>(1,static_cast<uint32_t>(avail10MBs*.2));#else// 40% of space up to 500 MB (50 MB min)sz10MBs+=std::max<uint32_t>(5,static_cast<uint32_t>(avail10MBs*.4));#endifreturnstd::min<uint32_t>(maxSize,sz10MBs*10*1024);}/* Computes our best guess for the default size of the user's disk cache, * based on the amount of space they have free on their hard drive. * We use a tiered scheme: the more space available, * the larger the disk cache will be. However, we do not want * to enable the disk cache to grow to an unbounded size, so the larger the * user's available space is, the smaller of a percentage we take. We set a * lower bound of 50MB and an upper bound of 1GB. * *@param: None. *@return: The size that the user's disk cache should default to, in kBytes. */uint32_tnsCacheProfilePrefObserver::GetSmartCacheSize(constnsAString&cachePath,uint32_tcurrentSize,boolshouldUseOldMaxSmartSize){// Check for free space on device where cache directory livesnsresultrv;nsCOMPtr<nsIFile>cacheDirectory(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID,&rv));if(NS_FAILED(rv)||!cacheDirectory)returnDEFAULT_CACHE_SIZE;rv=cacheDirectory->InitWithPath(cachePath);if(NS_FAILED(rv))returnDEFAULT_CACHE_SIZE;int64_tbytesAvailable;rv=cacheDirectory->GetDiskSpaceAvailable(&bytesAvailable);if(NS_FAILED(rv))returnDEFAULT_CACHE_SIZE;returnSmartCacheSize(static_cast<uint32_t>((bytesAvailable/1024)+currentSize),shouldUseOldMaxSmartSize);}/* Determine if we are permitted to dynamically size the user's disk cache based * on their disk space available. We may do this so long as the pref * smart_size.enabled is true. */boolnsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch*branch,boolfirstRun){nsresultrv;if(firstRun){// check if user has set cache size in the pastbooluserSet;rv=branch->PrefHasUserValue(DISK_CACHE_CAPACITY_PREF,&userSet);if(NS_FAILED(rv))userSet=true;if(userSet){int32_toldCapacity;// If user explicitly set cache size to be smaller than old default// of 50 MB, then keep user's value. Otherwise use smart sizing.rv=branch->GetIntPref(DISK_CACHE_CAPACITY_PREF,&oldCapacity);if(oldCapacity<PRE_GECKO_2_0_DEFAULT_CACHE_SIZE){mSmartSizeEnabled=false;branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,mSmartSizeEnabled);returnmSmartSizeEnabled;}}// Set manual setting to MAX cache size as starting val for any// adjustment by user: (bug 559942 comment 65)int32_tmaxSize=mShouldUseOldMaxSmartSize?OLD_MAX_CACHE_SIZE:MAX_CACHE_SIZE;branch->SetIntPref(DISK_CACHE_CAPACITY_PREF,maxSize);}rv=branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,&mSmartSizeEnabled);if(NS_FAILED(rv))mSmartSizeEnabled=false;returnmSmartSizeEnabled;}nsresultnsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch*branch){nsresultrv=NS_OK;// read disk cache device prefsmDiskCacheEnabled=true;// presume disk cache is enabled(void)branch->GetBoolPref(DISK_CACHE_ENABLE_PREF,&mDiskCacheEnabled);mDiskCacheCapacity=DISK_CACHE_CAPACITY;(void)branch->GetIntPref(DISK_CACHE_CAPACITY_PREF,&mDiskCacheCapacity);mDiskCacheCapacity=std::max(0,mDiskCacheCapacity);(void)branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,&mDiskCacheMaxEntrySize);mDiskCacheMaxEntrySize=std::max(-1,mDiskCacheMaxEntrySize);(void)branch->GetComplexValue(DISK_CACHE_DIR_PREF,// ignore errorNS_GET_IID(nsIFile),getter_AddRefs(mDiskCacheParentDirectory));(void)branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,&mShouldUseOldMaxSmartSize);if(!mDiskCacheParentDirectory){nsCOMPtr<nsIFile>directory;// try to get the disk cache parent directoryrv=NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,getter_AddRefs(directory));if(NS_FAILED(rv)){// try to get the profile directory (there may not be a profile yet)nsCOMPtr<nsIFile>profDir;NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,getter_AddRefs(profDir));NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,getter_AddRefs(directory));if(!directory)directory=profDir;elseif(profDir){nsCacheService::MoveOrRemoveDiskCache(profDir,directory,"Cache");}}// use file cache in build tree only if asked, to avoid cache dir litterif(!directory&&PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")){rv=NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,getter_AddRefs(directory));}if(directory)mDiskCacheParentDirectory=do_QueryInterface(directory,&rv);}if(mDiskCacheParentDirectory){boolfirstSmartSizeRun;rv=branch->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,&firstSmartSizeRun);if(NS_FAILED(rv))firstSmartSizeRun=false;if(PermittedToSmartSize(branch,firstSmartSizeRun)){// Avoid evictions: use previous cache size until smart size event// updates mDiskCacheCapacityrv=branch->GetIntPref(firstSmartSizeRun?DISK_CACHE_CAPACITY_PREF:DISK_CACHE_SMART_SIZE_PREF,&mDiskCacheCapacity);if(NS_FAILED(rv))mDiskCacheCapacity=DEFAULT_CACHE_SIZE;}if(firstSmartSizeRun){// It is no longer our first runrv=branch->SetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,false);if(NS_FAILED(rv))NS_WARNING("Failed setting first_run pref in ReadPrefs.");}}// read offline cache device prefsmOfflineCacheEnabled=true;// presume offline cache is enabled(void)branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,&mOfflineCacheEnabled);mOfflineCacheCapacity=OFFLINE_CACHE_CAPACITY;(void)branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF,&mOfflineCacheCapacity);mOfflineCacheCapacity=std::max(0,mOfflineCacheCapacity);(void)branch->GetComplexValue(OFFLINE_CACHE_DIR_PREF,// ignore errorNS_GET_IID(nsIFile),getter_AddRefs(mOfflineCacheParentDirectory));if(!mOfflineCacheParentDirectory){nsCOMPtr<nsIFile>directory;// try to get the offline cache parent directoryrv=NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,getter_AddRefs(directory));if(NS_FAILED(rv)){// try to get the profile directory (there may not be a profile yet)nsCOMPtr<nsIFile>profDir;NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,getter_AddRefs(profDir));NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,getter_AddRefs(directory));if(!directory)directory=profDir;elseif(profDir){nsCacheService::MoveOrRemoveDiskCache(profDir,directory,"OfflineCache");}}#if DEBUGif(!directory){// use current process directory during developmentrv=NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,getter_AddRefs(directory));}#endifif(directory)mOfflineCacheParentDirectory=do_QueryInterface(directory,&rv);}// read memory cache device prefs(void)branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF,&mMemoryCacheEnabled);mMemoryCacheCapacity=-1;(void)branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,&mMemoryCacheCapacity);(void)branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,&mMemoryCacheMaxEntrySize);mMemoryCacheMaxEntrySize=std::max(-1,mMemoryCacheMaxEntrySize);// read cache compression level prefmCacheCompressionLevel=CACHE_COMPRESSION_LEVEL;(void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,&mCacheCompressionLevel);mCacheCompressionLevel=std::max(0,mCacheCompressionLevel);mCacheCompressionLevel=std::min(9,mCacheCompressionLevel);// read cache shutdown sanitization prefs(void)branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,&mSanitizeOnShutdown);(void)branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,&mClearCacheOnShutdown);returnrv;}nsresultnsCacheService::DispatchToCacheIOThread(nsIRunnable*event){if(!gService||!gService->mCacheIOThread)returnNS_ERROR_NOT_AVAILABLE;returngService->mCacheIOThread->Dispatch(event,NS_DISPATCH_NORMAL);}nsresultnsCacheService::SyncWithCacheIOThread(){if(!gService||!gService->mCacheIOThread)returnNS_ERROR_NOT_AVAILABLE;gService->mLock.AssertCurrentThreadOwns();nsCOMPtr<nsIRunnable>event=newnsBlockOnCacheThreadEvent();// dispatch event - it will notify the monitor when it's donensresultrv=gService->mCacheIOThread->Dispatch(event,NS_DISPATCH_NORMAL);if(NS_FAILED(rv)){NS_WARNING("Failed dispatching block-event");returnNS_ERROR_UNEXPECTED;}// wait until notified, then returngService->mNotified=false;while(!gService->mNotified){gService->mCondVar.Wait();}returnNS_OK;}boolnsCacheProfilePrefObserver::DiskCacheEnabled(){if((mDiskCacheCapacity==0)||(!mDiskCacheParentDirectory))returnfalse;returnmDiskCacheEnabled&&(!mSanitizeOnShutdown||!mClearCacheOnShutdown);}boolnsCacheProfilePrefObserver::OfflineCacheEnabled(){if((mOfflineCacheCapacity==0)||(!mOfflineCacheParentDirectory))returnfalse;returnmOfflineCacheEnabled;}boolnsCacheProfilePrefObserver::MemoryCacheEnabled(){if(mMemoryCacheCapacity==0)returnfalse;returnmMemoryCacheEnabled;}/** * MemoryCacheCapacity * * If the browser.cache.memory.capacity preference is positive, we use that * value for the amount of memory available for the cache. * * If browser.cache.memory.capacity is zero, the memory cache is disabled. * * If browser.cache.memory.capacity is negative or not present, we use a * formula that grows less than linearly with the amount of system memory, * with an upper limit on the cache size. No matter how much physical RAM is * present, the default cache size would not exceed 32 MB. This maximum would * apply only to systems with more than 4 GB of RAM (e.g. terminal servers) * * RAM Cache * --- ----- * 32 Mb 2 Mb * 64 Mb 4 Mb * 128 Mb 6 Mb * 256 Mb 10 Mb * 512 Mb 14 Mb * 1024 Mb 18 Mb * 2048 Mb 24 Mb * 4096 Mb 30 Mb * * The equation for this is (for cache size C and memory size K (kbytes)): * x = log2(K) - 14 * C = x^2/3 + x + 2/3 + 0.1 (0.1 for rounding) * if (C > 32) C = 32 */int32_tnsCacheProfilePrefObserver::MemoryCacheCapacity(){int32_tcapacity=mMemoryCacheCapacity;if(capacity>=0){CACHE_LOG_DEBUG(("Memory cache capacity forced to %d\n",capacity));returncapacity;}staticuint64_tbytes=PR_GetPhysicalMemorySize();CACHE_LOG_DEBUG(("Physical Memory size is %"PRIu64"\n",bytes));// If getting the physical memory failed, arbitrarily assume// 32 MB of RAM. We use a low default to have a reasonable// size on all the devices we support.if(bytes==0)bytes=32*1024*1024;// Conversion from unsigned int64_t to double doesn't work on all platforms.// We need to truncate the value at INT64_MAX to make sure we don't// overflow.if(bytes>INT64_MAX)bytes=INT64_MAX;uint64_tkbytes=bytes>>10;doublekBytesD=double(kbytes);doublex=log(kBytesD)/log(2.0)-14;if(x>0){capacity=(int32_t)(x*x/3.0+x+2.0/3+0.1);// 0.1 for roundingif(capacity>32)capacity=32;capacity*=1024;}else{capacity=0;}returncapacity;}int32_tnsCacheProfilePrefObserver::CacheCompressionLevel(){returnmCacheCompressionLevel;}/****************************************************************************** * nsProcessRequestEvent *****************************************************************************/classnsProcessRequestEvent:publicRunnable{public:explicitnsProcessRequestEvent(nsCacheRequest*aRequest):mozilla::Runnable("nsProcessRequestEvent"){mRequest=aRequest;}NS_IMETHODRun()override{nsresultrv;NS_ASSERTION(mRequest->mListener,"Sync OpenCacheEntry() posted to background thread!");nsCacheServiceAutoLocklock(LOCK_TELEM(NSPROCESSREQUESTEVENT_RUN));rv=nsCacheService::gService->ProcessRequest(mRequest,false,nullptr);// Don't delete the request if it was queuedif(!(mRequest->IsBlocking()&&rv==NS_ERROR_CACHE_WAIT_FOR_VALIDATION))deletemRequest;returnNS_OK;}protected:virtual~nsProcessRequestEvent(){}private:nsCacheRequest*mRequest;};/****************************************************************************** * nsDoomEvent *****************************************************************************/classnsDoomEvent:publicRunnable{public:nsDoomEvent(nsCacheSession*session,constnsACString&key,nsICacheListener*listener):mozilla::Runnable("nsDoomEvent"){mKey=*session->ClientID();mKey.Append(':');mKey.Append(key);mStoragePolicy=session->StoragePolicy();mListener=listener;mEventTarget=GetCurrentThreadEventTarget();// We addref the listener here and release it in nsNotifyDoomListener// on the callers thread. If posting of nsNotifyDoomListener event fails// we leak the listener which is better than releasing it on a wrong// thread.NS_IF_ADDREF(mListener);}NS_IMETHODRun()override{nsCacheServiceAutoLocklock;boolfoundActive=true;nsresultstatus=NS_ERROR_NOT_AVAILABLE;nsCacheEntry*entry;entry=nsCacheService::gService->mActiveEntries.GetEntry(&mKey);if(!entry){boolcollision=false;foundActive=false;entry=nsCacheService::gService->SearchCacheDevices(&mKey,mStoragePolicy,&collision);}if(entry){status=NS_OK;nsCacheService::gService->DoomEntry_Internal(entry,foundActive);}if(mListener){mEventTarget->Dispatch(newnsNotifyDoomListener(mListener,status),NS_DISPATCH_NORMAL);// posted event will release the reference on the correct threadmListener=nullptr;}returnNS_OK;}private:nsCStringmKey;nsCacheStoragePolicymStoragePolicy;nsICacheListener*mListener;nsCOMPtr<nsIEventTarget>mEventTarget;};/****************************************************************************** * nsCacheService *****************************************************************************/nsCacheService*nsCacheService::gService=nullptr;NS_IMPL_ISUPPORTS(nsCacheService,nsICacheService,nsICacheServiceInternal,nsIMemoryReporter)nsCacheService::nsCacheService():mObserver(nullptr),mLock("nsCacheService.mLock"),mCondVar(mLock,"nsCacheService.mCondVar"),mNotified(false),mTimeStampLock("nsCacheService.mTimeStampLock"),mInitialized(false),mClearingEntries(false),mEnableMemoryDevice(true),mEnableDiskDevice(true),mMemoryDevice(nullptr),mDiskDevice(nullptr),mOfflineDevice(nullptr),mTotalEntries(0),mCacheHits(0),mCacheMisses(0),mMaxKeyLength(0),mMaxDataSize(0),mMaxMetaSize(0),mDeactivateFailures(0),mDeactivatedUnboundEntries(0){NS_ASSERTION(gService==nullptr,"multiple nsCacheService instances!");gService=this;// create list of cache devicesPR_INIT_CLIST(&mDoomedEntries);}nsCacheService::~nsCacheService(){if(mInitialized)// Shutdown hasn't been called yet.(void)Shutdown();if(mObserver){mObserver->Remove();NS_RELEASE(mObserver);}gService=nullptr;}nsresultnsCacheService::Init(){// Thie method must be called on the main thread because mCacheIOThread must// only be modified on the main thread.if(!NS_IsMainThread()){NS_ERROR("nsCacheService::Init called off the main thread");returnNS_ERROR_NOT_SAME_THREAD;}NS_ASSERTION(!mInitialized,"nsCacheService already initialized.");if(mInitialized)returnNS_ERROR_ALREADY_INITIALIZED;if(mozilla::net::IsNeckoChild()){returnNS_ERROR_UNEXPECTED;}nsresultrv;mStorageService=do_GetService("@mozilla.org/storage/service;1",&rv);NS_ENSURE_SUCCESS(rv,rv);rv=NS_NewNamedThread("Cache I/O",getter_AddRefs(mCacheIOThread));if(NS_FAILED(rv)){MOZ_CRASH("Can't create cache IO thread");}rv=nsDeleteDir::Init();if(NS_FAILED(rv)){NS_WARNING("Can't initialize nsDeleteDir");}// initialize hashtable for active cache entriesmActiveEntries.Init();// create profile/preference observerif(!mObserver){mObserver=newnsCacheProfilePrefObserver();NS_ADDREF(mObserver);mObserver->Install();}mEnableDiskDevice=mObserver->DiskCacheEnabled();mEnableOfflineDevice=mObserver->OfflineCacheEnabled();mEnableMemoryDevice=mObserver->MemoryCacheEnabled();RegisterWeakMemoryReporter(this);mInitialized=true;returnNS_OK;}voidnsCacheService::Shutdown(){// This method must be called on the main thread because mCacheIOThread must// only be modified on the main thread.if(!NS_IsMainThread()){MOZ_CRASH("nsCacheService::Shutdown called off the main thread");}nsCOMPtr<nsIThread>cacheIOThread;Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN>totalTimer;boolshouldSanitize=false;nsCOMPtr<nsIFile>parentDir;{nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN));NS_ASSERTION(mInitialized,"can't shutdown nsCacheService unless it has been initialized.");if(!mInitialized)return;mClearingEntries=true;DoomActiveEntries(nullptr);}CloseAllStreams();UnregisterWeakMemoryReporter(this);{nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_SHUTDOWN));NS_ASSERTION(mInitialized,"Bad state");mInitialized=false;// Clear entriesClearDoomList();if(mSmartSizeTimer){mSmartSizeTimer->Cancel();mSmartSizeTimer=nullptr;}// Make sure to wait for any pending cache-operations before// proceeding with destructive actions (bug #620660)(void)SyncWithCacheIOThread();mActiveEntries.Shutdown();// obtain the disk cache directory in case we need to sanitize itparentDir=mObserver->DiskCacheParentDirectory();shouldSanitize=mObserver->SanitizeAtShutdown();// deallocate memory and disk cachesdeletemMemoryDevice;mMemoryDevice=nullptr;deletemDiskDevice;mDiskDevice=nullptr;if(mOfflineDevice)mOfflineDevice->Shutdown();NS_IF_RELEASE(mOfflineDevice);for(autoiter=mCustomOfflineDevices.Iter();!iter.Done();iter.Next()){iter.Data()->Shutdown();iter.Remove();}LogCacheStatistics();mClearingEntries=false;mCacheIOThread.swap(cacheIOThread);}if(cacheIOThread)nsShutdownThread::BlockingShutdown(cacheIOThread);if(shouldSanitize){nsresultrv=parentDir->AppendNative(NS_LITERAL_CSTRING("Cache"));if(NS_SUCCEEDED(rv)){boolexists;if(NS_SUCCEEDED(parentDir->Exists(&exists))&&exists)nsDeleteDir::DeleteDir(parentDir,false);}Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_CLEAR_PRIVATE>timer;nsDeleteDir::Shutdown(shouldSanitize);}else{Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_DELETEDIR_SHUTDOWN>timer;nsDeleteDir::Shutdown(shouldSanitize);}}nsresultnsCacheService::Create(nsISupports*aOuter,constnsIID&aIID,void**aResult){nsresultrv;if(aOuter!=nullptr)returnNS_ERROR_NO_AGGREGATION;nsCacheService*cacheService=newnsCacheService();if(cacheService==nullptr)returnNS_ERROR_OUT_OF_MEMORY;NS_ADDREF(cacheService);rv=cacheService->Init();if(NS_SUCCEEDED(rv)){rv=cacheService->QueryInterface(aIID,aResult);}NS_RELEASE(cacheService);returnrv;}NS_IMETHODIMPnsCacheService::CreateSession(constchar*clientID,nsCacheStoragePolicystoragePolicy,boolstreamBased,nsICacheSession**result){*result=nullptr;if(net::CacheObserver::UseNewCache())returnNS_ERROR_NOT_IMPLEMENTED;returnCreateSessionInternal(clientID,storagePolicy,streamBased,result);}nsresultnsCacheService::CreateSessionInternal(constchar*clientID,nsCacheStoragePolicystoragePolicy,boolstreamBased,nsICacheSession**result){RefPtr<nsCacheSession>session=newnsCacheSession(clientID,storagePolicy,streamBased);session.forget(result);returnNS_OK;}nsresultnsCacheService::EvictEntriesForSession(nsCacheSession*session){NS_ASSERTION(gService,"nsCacheService::gService is null.");returngService->EvictEntriesForClient(session->ClientID()->get(),session->StoragePolicy());}namespace{classEvictionNotifierRunnable:publicRunnable{public:explicitEvictionNotifierRunnable(nsISupports*aSubject):mozilla::Runnable("EvictionNotifierRunnable"),mSubject(aSubject){}NS_DECL_NSIRUNNABLEprivate:nsCOMPtr<nsISupports>mSubject;};NS_IMETHODIMPEvictionNotifierRunnable::Run(){nsCOMPtr<nsIObserverService>obsSvc=mozilla::services::GetObserverService();if(obsSvc){obsSvc->NotifyObservers(mSubject,NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID,nullptr);}returnNS_OK;}}// namespacensresultnsCacheService::EvictEntriesForClient(constchar*clientID,nsCacheStoragePolicystoragePolicy){RefPtr<EvictionNotifierRunnable>r=newEvictionNotifierRunnable(NS_ISUPPORTS_CAST(nsICacheService*,this));NS_DispatchToMainThread(r);nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_EVICTENTRIESFORCLIENT));nsresultres=NS_OK;if(storagePolicy==nsICache::STORE_ANYWHERE||storagePolicy==nsICache::STORE_ON_DISK){if(mEnableDiskDevice){nsresultrv=NS_OK;if(!mDiskDevice)rv=CreateDiskDevice();if(mDiskDevice)rv=mDiskDevice->EvictEntries(clientID);if(NS_FAILED(rv))res=rv;}}// Only clear the offline cache if it has been specifically asked for.if(storagePolicy==nsICache::STORE_OFFLINE){if(mEnableOfflineDevice){nsresultrv=NS_OK;if(!mOfflineDevice)rv=CreateOfflineDevice();if(mOfflineDevice)rv=mOfflineDevice->EvictEntries(clientID);if(NS_FAILED(rv))res=rv;}}if(storagePolicy==nsICache::STORE_ANYWHERE||storagePolicy==nsICache::STORE_IN_MEMORY){// If there is no memory device, there is no need to evict it...if(mMemoryDevice){nsresultrv=mMemoryDevice->EvictEntries(clientID);if(NS_FAILED(rv))res=rv;}}returnres;}nsresultnsCacheService::IsStorageEnabledForPolicy(nsCacheStoragePolicystoragePolicy,bool*result){if(gService==nullptr)returnNS_ERROR_NOT_AVAILABLE;nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_ISSTORAGEENABLEDFORPOLICY));*result=gService->IsStorageEnabledForPolicy_Locked(storagePolicy);returnNS_OK;}nsresultnsCacheService::DoomEntry(nsCacheSession*session,constnsACString&key,nsICacheListener*listener){CACHE_LOG_DEBUG(("Dooming entry for session %p, key %s\n",session,PromiseFlatCString(key).get()));if(!gService||!gService->mInitialized)returnNS_ERROR_NOT_INITIALIZED;returnDispatchToCacheIOThread(newnsDoomEvent(session,key,listener));}boolnsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicystoragePolicy){if(gService->mEnableMemoryDevice&&(storagePolicy==nsICache::STORE_ANYWHERE||storagePolicy==nsICache::STORE_IN_MEMORY)){returntrue;}if(gService->mEnableDiskDevice&&(storagePolicy==nsICache::STORE_ANYWHERE||storagePolicy==nsICache::STORE_ON_DISK)){returntrue;}if(gService->mEnableOfflineDevice&&storagePolicy==nsICache::STORE_OFFLINE){returntrue;}returnfalse;}NS_IMETHODIMPnsCacheService::VisitEntries(nsICacheVisitor*visitor){if(net::CacheObserver::UseNewCache())returnNS_ERROR_NOT_IMPLEMENTED;returnVisitEntriesInternal(visitor);}nsresultnsCacheService::VisitEntriesInternal(nsICacheVisitor*visitor){NS_ENSURE_ARG_POINTER(visitor);nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_VISITENTRIES));if(!(mEnableDiskDevice||mEnableMemoryDevice))returnNS_ERROR_NOT_AVAILABLE;// XXX record the fact that a visitation is in progress, // XXX i.e. keep list of visitors in progress.nsresultrv=NS_OK;// If there is no memory device, there are then also no entries to visit...if(mMemoryDevice){rv=mMemoryDevice->Visit(visitor);if(NS_FAILED(rv))returnrv;}if(mEnableDiskDevice){if(!mDiskDevice){rv=CreateDiskDevice();if(NS_FAILED(rv))returnrv;}rv=mDiskDevice->Visit(visitor);if(NS_FAILED(rv))returnrv;}if(mEnableOfflineDevice){if(!mOfflineDevice){rv=CreateOfflineDevice();if(NS_FAILED(rv))returnrv;}rv=mOfflineDevice->Visit(visitor);if(NS_FAILED(rv))returnrv;}// XXX notify any shutdown process that visitation is complete for THIS visitor.// XXX keep queue of visitorsreturnNS_OK;}voidnsCacheService::FireClearNetworkCacheStoredAnywhereNotification(){MOZ_ASSERT(NS_IsMainThread());nsCOMPtr<nsIObserverService>obsvc=mozilla::services::GetObserverService();if(obsvc){obsvc->NotifyObservers(nullptr,"network-clear-cache-stored-anywhere",nullptr);}}NS_IMETHODIMPnsCacheService::EvictEntries(nsCacheStoragePolicystoragePolicy){if(net::CacheObserver::UseNewCache())returnNS_ERROR_NOT_IMPLEMENTED;returnEvictEntriesInternal(storagePolicy);}nsresultnsCacheService::EvictEntriesInternal(nsCacheStoragePolicystoragePolicy){if(storagePolicy==nsICache::STORE_ANYWHERE){// if not called on main thread, dispatch the notification to the main thread to notify observersif(!NS_IsMainThread()){nsCOMPtr<nsIRunnable>event=NewRunnableMethod("nsCacheService::FireClearNetworkCacheStoredAnywhereNotification",this,&nsCacheService::FireClearNetworkCacheStoredAnywhereNotification);NS_DispatchToMainThread(event);}else{// else you're already on main thread - notify observersFireClearNetworkCacheStoredAnywhereNotification();}}returnEvictEntriesForClient(nullptr,storagePolicy);}NS_IMETHODIMPnsCacheService::GetCacheIOTarget(nsIEventTarget**aCacheIOTarget){NS_ENSURE_ARG_POINTER(aCacheIOTarget);// Because mCacheIOThread can only be changed on the main thread, it can be// read from the main thread without the lock. This is useful to prevent// blocking the main thread on other cache operations.if(!NS_IsMainThread()){Lock(LOCK_TELEM(NSCACHESERVICE_GETCACHEIOTARGET));}nsresultrv;if(mCacheIOThread){NS_ADDREF(*aCacheIOTarget=mCacheIOThread);rv=NS_OK;}else{*aCacheIOTarget=nullptr;rv=NS_ERROR_NOT_AVAILABLE;}if(!NS_IsMainThread()){Unlock();}returnrv;}NS_IMETHODIMPnsCacheService::GetLockHeldTime(double*aLockHeldTime){MutexAutoLocklock(mTimeStampLock);if(mLockAcquiredTimeStamp.IsNull()){*aLockHeldTime=0.0;}else{*aLockHeldTime=(TimeStamp::Now()-mLockAcquiredTimeStamp).ToMilliseconds();}returnNS_OK;}/** * Internal Methods */nsresultnsCacheService::CreateDiskDevice(){if(!mInitialized)returnNS_ERROR_NOT_AVAILABLE;if(!mEnableDiskDevice)returnNS_ERROR_NOT_AVAILABLE;if(mDiskDevice)returnNS_OK;mDiskDevice=newnsDiskCacheDevice;if(!mDiskDevice)returnNS_ERROR_OUT_OF_MEMORY;// set the preferencesmDiskDevice->SetCacheParentDirectory(mObserver->DiskCacheParentDirectory());mDiskDevice->SetCapacity(mObserver->DiskCacheCapacity());mDiskDevice->SetMaxEntrySize(mObserver->DiskCacheMaxEntrySize());nsresultrv=mDiskDevice->Init();if(NS_FAILED(rv)){#if DEBUGprintf("###\n");printf("### mDiskDevice->Init() failed (0x%.8x)\n",static_cast<uint32_t>(rv));printf("### - disabling disk cache for this session.\n");printf("###\n");#endifmEnableDiskDevice=false;deletemDiskDevice;mDiskDevice=nullptr;returnrv;}NS_ASSERTION(!mSmartSizeTimer,"Smartsize timer was already fired!");// Disk device is usually created during the startup. Delay smart size// calculation to avoid possible massive IO caused by eviction of entries// in case the new smart size is smaller than current cache usage.mSmartSizeTimer=do_CreateInstance("@mozilla.org/timer;1",&rv);if(NS_SUCCEEDED(rv)){rv=mSmartSizeTimer->InitWithCallback(newnsSetDiskSmartSizeCallback(),1000*60*3,nsITimer::TYPE_ONE_SHOT);if(NS_FAILED(rv)){NS_WARNING("Failed to post smart size timer");mSmartSizeTimer=nullptr;}}else{NS_WARNING("Can't create smart size timer");}// Ignore state of the timer and return success since the purpose of the// method (create the disk-device) has been fulfilledreturnNS_OK;}// Runnable sent from cache thread to main threadclassnsDisableOldMaxSmartSizePrefEvent:publicRunnable{public:nsDisableOldMaxSmartSizePrefEvent():mozilla::Runnable("nsDisableOldMaxSmartSizePrefEvent"){}NS_IMETHODRun()override{// Main thread may have already called nsCacheService::Shutdownif(!nsCacheService::IsInitialized())returnNS_ERROR_NOT_AVAILABLE;nsCOMPtr<nsIPrefBranch>branch=do_GetService(NS_PREFSERVICE_CONTRACTID);if(!branch){returnNS_ERROR_NOT_AVAILABLE;}nsresultrv=branch->SetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,false);if(NS_FAILED(rv)){NS_WARNING("Failed to disable old max smart size");returnrv;}// It is safe to call SetDiskSmartSize_Locked() without holding the lock// when we are on main thread and nsCacheService is initialized.nsCacheService::gService->SetDiskSmartSize_Locked();if(nsCacheService::gService->mObserver->PermittedToSmartSize(branch,false)){rv=branch->SetIntPref(DISK_CACHE_CAPACITY_PREF,MAX_CACHE_SIZE);if(NS_FAILED(rv)){NS_WARNING("Failed to set cache capacity pref");}}returnNS_OK;}};voidnsCacheService::MarkStartingFresh(){if(!gService||!gService->mObserver->ShouldUseOldMaxSmartSize()){// Already using new max, nothing to do herereturn;}gService->mObserver->SetUseNewMaxSmartSize(true);// We always dispatch an event here because we don't want to deal with lock// reentrance issues.NS_DispatchToMainThread(newnsDisableOldMaxSmartSizePrefEvent());}nsresultnsCacheService::GetOfflineDevice(nsOfflineCacheDevice**aDevice){if(!mOfflineDevice){nsresultrv=CreateOfflineDevice();NS_ENSURE_SUCCESS(rv,rv);}NS_ADDREF(*aDevice=mOfflineDevice);returnNS_OK;}nsresultnsCacheService::GetCustomOfflineDevice(nsIFile*aProfileDir,int32_taQuota,nsOfflineCacheDevice**aDevice){nsresultrv;nsAutoStringprofilePath;rv=aProfileDir->GetPath(profilePath);NS_ENSURE_SUCCESS(rv,rv);if(!mCustomOfflineDevices.Get(profilePath,aDevice)){rv=CreateCustomOfflineDevice(aProfileDir,aQuota,aDevice);NS_ENSURE_SUCCESS(rv,rv);(*aDevice)->SetAutoShutdown();mCustomOfflineDevices.Put(profilePath,*aDevice);}returnNS_OK;}nsresultnsCacheService::CreateOfflineDevice(){CACHE_LOG_INFO(("Creating default offline device"));if(mOfflineDevice)returnNS_OK;if(!nsCacheService::IsInitialized()){returnNS_ERROR_NOT_AVAILABLE;}nsresultrv=CreateCustomOfflineDevice(mObserver->OfflineCacheParentDirectory(),mObserver->OfflineCacheCapacity(),&mOfflineDevice);NS_ENSURE_SUCCESS(rv,rv);returnNS_OK;}nsresultnsCacheService::CreateCustomOfflineDevice(nsIFile*aProfileDir,int32_taQuota,nsOfflineCacheDevice**aDevice){NS_ENSURE_ARG(aProfileDir);if(MOZ_LOG_TEST(gCacheLog,LogLevel::Info)){nsAutoCStringprofilePath;aProfileDir->GetNativePath(profilePath);CACHE_LOG_INFO(("Creating custom offline device, %s, %d",profilePath.BeginReading(),aQuota));}if(!mInitialized)returnNS_ERROR_NOT_AVAILABLE;if(!mEnableOfflineDevice)returnNS_ERROR_NOT_AVAILABLE;*aDevice=newnsOfflineCacheDevice;NS_ADDREF(*aDevice);// set the preferences(*aDevice)->SetCacheParentDirectory(aProfileDir);(*aDevice)->SetCapacity(aQuota);nsresultrv=(*aDevice)->InitWithSqlite(mStorageService);if(NS_FAILED(rv)){CACHE_LOG_DEBUG(("OfflineDevice->InitWithSqlite() failed (0x%.8"PRIx32")\n",static_cast<uint32_t>(rv)));CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n"));NS_RELEASE(*aDevice);}returnrv;}nsresultnsCacheService::CreateMemoryDevice(){if(!mInitialized)returnNS_ERROR_NOT_AVAILABLE;if(!mEnableMemoryDevice)returnNS_ERROR_NOT_AVAILABLE;if(mMemoryDevice)returnNS_OK;mMemoryDevice=newnsMemoryCacheDevice;if(!mMemoryDevice)returnNS_ERROR_OUT_OF_MEMORY;// set preferenceint32_tcapacity=mObserver->MemoryCacheCapacity();CACHE_LOG_DEBUG(("Creating memory device with capacity %d\n",capacity));mMemoryDevice->SetCapacity(capacity);mMemoryDevice->SetMaxEntrySize(mObserver->MemoryCacheMaxEntrySize());nsresultrv=mMemoryDevice->Init();if(NS_FAILED(rv)){NS_WARNING("Initialization of Memory Cache failed.");deletemMemoryDevice;mMemoryDevice=nullptr;}returnrv;}nsresultnsCacheService::RemoveCustomOfflineDevice(nsOfflineCacheDevice*aDevice){nsCOMPtr<nsIFile>profileDir=aDevice->BaseDirectory();if(!profileDir)returnNS_ERROR_UNEXPECTED;nsAutoStringprofilePath;nsresultrv=profileDir->GetPath(profilePath);NS_ENSURE_SUCCESS(rv,rv);mCustomOfflineDevices.Remove(profilePath);returnNS_OK;}nsresultnsCacheService::CreateRequest(nsCacheSession*session,constnsACString&clientKey,nsCacheAccessModeaccessRequested,boolblockingMode,nsICacheListener*listener,nsCacheRequest**request){NS_ASSERTION(request,"CreateRequest: request is null");nsAutoCStringkey(*session->ClientID());key.Append(':');key.Append(clientKey);if(mMaxKeyLength<key.Length())mMaxKeyLength=key.Length();// create request*request=newnsCacheRequest(key,listener,accessRequested,blockingMode,session);if(!listener)returnNS_OK;// we're sync, we're done.// get the request's thread(*request)->mEventTarget=GetCurrentThreadEventTarget();returnNS_OK;}classnsCacheListenerEvent:publicRunnable{public:nsCacheListenerEvent(nsICacheListener*listener,nsICacheEntryDescriptor*descriptor,nsCacheAccessModeaccessGranted,nsresultstatus):mozilla::Runnable("nsCacheListenerEvent"),mListener(listener)// transfers reference,mDescriptor(descriptor)// transfers reference (may be null),mAccessGranted(accessGranted),mStatus(status){}NS_IMETHODRun()override{mListener->OnCacheEntryAvailable(mDescriptor,mAccessGranted,mStatus);NS_RELEASE(mListener);NS_IF_RELEASE(mDescriptor);returnNS_OK;}private:// We explicitly leak mListener or mDescriptor if Run is not called// because otherwise we cannot guarantee that they are destroyed on// the right thread.nsICacheListener*mListener;nsICacheEntryDescriptor*mDescriptor;nsCacheAccessModemAccessGranted;nsresultmStatus;};nsresultnsCacheService::NotifyListener(nsCacheRequest*request,nsICacheEntryDescriptor*descriptor,nsCacheAccessModeaccessGranted,nsresultstatus){NS_ASSERTION(request->mEventTarget,"no thread set in async request!");// Swap ownership, and release listener on target thread...nsICacheListener*listener=request->mListener;request->mListener=nullptr;nsCOMPtr<nsIRunnable>ev=newnsCacheListenerEvent(listener,descriptor,accessGranted,status);if(!ev){// Better to leak listener and descriptor if we fail because we don't// want to destroy them inside the cache service lock or on potentially// the wrong thread.returnNS_ERROR_OUT_OF_MEMORY;}returnrequest->mEventTarget->Dispatch(ev,NS_DISPATCH_NORMAL);}nsresultnsCacheService::ProcessRequest(nsCacheRequest*request,boolcalledFromOpenCacheEntry,nsICacheEntryDescriptor**result){// !!! must be called with mLock held !!!nsresultrv;nsCacheEntry*entry=nullptr;nsCacheEntry*doomedEntry=nullptr;nsCacheAccessModeaccessGranted=nsICache::ACCESS_NONE;if(result)*result=nullptr;while(1){// Activate entry looprv=ActivateEntry(request,&entry,&doomedEntry);// get the entry for this requestif(NS_FAILED(rv))break;while(1){// Request Access loopNS_ASSERTION(entry,"no entry in Request Access loop!");// entry->RequestAccess queues request on entryrv=entry->RequestAccess(request,&accessGranted);if(rv!=NS_ERROR_CACHE_WAIT_FOR_VALIDATION)break;if(request->IsBlocking()){if(request->mListener){// async exits - validate, doom, or close will resumereturnrv;}// XXX this is probably wrong...Unlock();rv=request->WaitForValidation();Lock(LOCK_TELEM(NSCACHESERVICE_PROCESSREQUEST));}PR_REMOVE_AND_INIT_LINK(request);if(NS_FAILED(rv))break;// non-blocking mode returns WAIT_FOR_VALIDATION error// okay, we're ready to process this request, request access again}if(rv!=NS_ERROR_CACHE_ENTRY_DOOMED)break;if(entry->IsNotInUse()){// this request was the last one keeping it around, so get rid of itDeactivateEntry(entry);}// loop back around to look for another entry}if(NS_SUCCEEDED(rv)&&request->mProfileDir){// Custom cache directory has been demanded. Preset the cache device.if(entry->StoragePolicy()!=nsICache::STORE_OFFLINE){// Failsafe check: this is implemented only for offline cache atm.rv=NS_ERROR_FAILURE;}else{RefPtr<nsOfflineCacheDevice>customCacheDevice;rv=GetCustomOfflineDevice(request->mProfileDir,-1,getter_AddRefs(customCacheDevice));if(NS_SUCCEEDED(rv))entry->SetCustomCacheDevice(customCacheDevice);}}nsICacheEntryDescriptor*descriptor=nullptr;if(NS_SUCCEEDED(rv))rv=entry->CreateDescriptor(request,accessGranted,&descriptor);// If doomedEntry is set, ActivatEntry() doomed an existing entry and// created a new one for that cache-key. However, any pending requests// on the doomed entry were not processed and we need to do that here.// This must be done after adding the created entry to list of active// entries (which is done in ActivateEntry()) otherwise the hashkeys crash// (see bug ##561313). It is also important to do this after creating a// descriptor for this request, or some other request may end up being// executed first for the newly created entry.// Finally, it is worth to emphasize that if doomedEntry is set,// ActivateEntry() created a new entry for the request, which will be// initialized by RequestAccess() and they both should have returned NS_OK.if(doomedEntry){(void)ProcessPendingRequests(doomedEntry);if(doomedEntry->IsNotInUse())DeactivateEntry(doomedEntry);doomedEntry=nullptr;}if(request->mListener){// Asynchronousif(NS_FAILED(rv)&&calledFromOpenCacheEntry&&request->IsBlocking())returnrv;// skip notifying listener, just return rv to caller// call listener to report error or descriptornsresultrv2=NotifyListener(request,descriptor,accessGranted,rv);if(NS_FAILED(rv2)&&NS_SUCCEEDED(rv)){rv=rv2;// trigger delete request}}else{// Synchronous*result=descriptor;}returnrv;}nsresultnsCacheService::OpenCacheEntry(nsCacheSession*session,constnsACString&key,nsCacheAccessModeaccessRequested,boolblockingMode,nsICacheListener*listener,nsICacheEntryDescriptor**result){CACHE_LOG_DEBUG(("Opening entry for session %p, key %s, mode %d, blocking %d\n",session,PromiseFlatCString(key).get(),accessRequested,blockingMode));if(result)*result=nullptr;if(!gService||!gService->mInitialized)returnNS_ERROR_NOT_INITIALIZED;nsCacheRequest*request=nullptr;nsresultrv=gService->CreateRequest(session,key,accessRequested,blockingMode,listener,&request);if(NS_FAILED(rv))returnrv;CACHE_LOG_DEBUG(("Created request %p\n",request));// Process the request on the background thread if we are on the main thread// and the the request is asynchronousif(NS_IsMainThread()&&listener&&gService->mCacheIOThread){nsCOMPtr<nsIRunnable>ev=newnsProcessRequestEvent(request);rv=DispatchToCacheIOThread(ev);// delete request if we didn't post the eventif(NS_FAILED(rv))deleterequest;}else{nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_OPENCACHEENTRY));rv=gService->ProcessRequest(request,true,result);// delete requests that have completedif(!(listener&&blockingMode&&(rv==NS_ERROR_CACHE_WAIT_FOR_VALIDATION)))deleterequest;}returnrv;}nsresultnsCacheService::ActivateEntry(nsCacheRequest*request,nsCacheEntry**result,nsCacheEntry**doomedEntry){CACHE_LOG_DEBUG(("Activate entry for request %p\n",request));if(!mInitialized||mClearingEntries)returnNS_ERROR_NOT_AVAILABLE;nsresultrv=NS_OK;NS_ASSERTION(request!=nullptr,"ActivateEntry called with no request");if(result)*result=nullptr;if(doomedEntry)*doomedEntry=nullptr;if((!request)||(!result)||(!doomedEntry))returnNS_ERROR_NULL_POINTER;// check if the request can be satisfiedif(!mEnableMemoryDevice&&!request->IsStreamBased())returnNS_ERROR_FAILURE;if(!IsStorageEnabledForPolicy_Locked(request->StoragePolicy()))returnNS_ERROR_FAILURE;// search active entries (including those not bound to device)nsCacheEntry*entry=mActiveEntries.GetEntry(&(request->mKey));CACHE_LOG_DEBUG(("Active entry for request %p is %p\n",request,entry));if(!entry){// search cache devices for entryboolcollision=false;entry=SearchCacheDevices(&(request->mKey),request->StoragePolicy(),&collision);CACHE_LOG_DEBUG(("Device search for request %p returned %p\n",request,entry));// When there is a hashkey collision just refuse to cache it...if(collision)returnNS_ERROR_CACHE_IN_USE;if(entry)entry->MarkInitialized();}else{NS_ASSERTION(entry->IsActive(),"Inactive entry found in mActiveEntries!");}if(entry){++mCacheHits;entry->Fetched();}else{++mCacheMisses;}if(entry&&((request->AccessRequested()==nsICache::ACCESS_WRITE)||((request->StoragePolicy()!=nsICache::STORE_OFFLINE)&&(entry->mExpirationTime<=SecondsFromPRTime(PR_Now())&&request->WillDoomEntriesIfExpired())))){// this is FORCE-WRITE request or the entry has expired// we doom entry without processing pending requests, but store it in// doomedEntry which causes pending requests to be processed belowrv=DoomEntry_Internal(entry,false);*doomedEntry=entry;if(NS_FAILED(rv)){// XXX what to do? Increment FailedDooms counter?}entry=nullptr;}if(!entry){if(!(request->AccessRequested()&nsICache::ACCESS_WRITE)){// this is a READ-ONLY requestrv=NS_ERROR_CACHE_KEY_NOT_FOUND;gotoerror;}entry=newnsCacheEntry(request->mKey,request->IsStreamBased(),request->StoragePolicy());if(!entry)returnNS_ERROR_OUT_OF_MEMORY;if(request->IsPrivate())entry->MarkPrivate();entry->Fetched();++mTotalEntries;// XXX we could perform an early bind in some cases based on storage policy}if(!entry->IsActive()){rv=mActiveEntries.AddEntry(entry);if(NS_FAILED(rv))gotoerror;CACHE_LOG_DEBUG(("Added entry %p to mActiveEntries\n",entry));entry->MarkActive();// mark entry active, because it's now in mActiveEntries}*result=entry;returnNS_OK;error:*result=nullptr;deleteentry;returnrv;}nsCacheEntry*nsCacheService::SearchCacheDevices(nsCString*key,nsCacheStoragePolicypolicy,bool*collision){Telemetry::AutoTimer<Telemetry::CACHE_DEVICE_SEARCH_2>timer;nsCacheEntry*entry=nullptr;CACHE_LOG_DEBUG(("mMemoryDevice: 0x%p\n",mMemoryDevice));*collision=false;if((policy==nsICache::STORE_ANYWHERE)||(policy==nsICache::STORE_IN_MEMORY)){// If there is no memory device, then there is nothing to search...if(mMemoryDevice){entry=mMemoryDevice->FindEntry(key,collision);CACHE_LOG_DEBUG(("Searching mMemoryDevice for key %s found: 0x%p, ""collision: %d\n",key->get(),entry,*collision));}}if(!entry&&((policy==nsICache::STORE_ANYWHERE)||(policy==nsICache::STORE_ON_DISK))){if(mEnableDiskDevice){if(!mDiskDevice){nsresultrv=CreateDiskDevice();if(NS_FAILED(rv))returnnullptr;}entry=mDiskDevice->FindEntry(key,collision);}}if(!entry&&(policy==nsICache::STORE_OFFLINE||(policy==nsICache::STORE_ANYWHERE&&gIOService->IsOffline()))){if(mEnableOfflineDevice){if(!mOfflineDevice){nsresultrv=CreateOfflineDevice();if(NS_FAILED(rv))returnnullptr;}entry=mOfflineDevice->FindEntry(key,collision);}}returnentry;}nsCacheDevice*nsCacheService::EnsureEntryHasDevice(nsCacheEntry*entry){nsCacheDevice*device=entry->CacheDevice();// return device if found, possibly null if the entry is doomed i.e prevent// doomed entries to bind to a device (see e.g. bugs #548406 and #596443)if(device||entry->IsDoomed())returndevice;int64_tpredictedDataSize=entry->PredictedDataSize();if(entry->IsStreamData()&&entry->IsAllowedOnDisk()&&mEnableDiskDevice){// this is the defaultif(!mDiskDevice){(void)CreateDiskDevice();// ignore the error (check for mDiskDevice instead)}if(mDiskDevice){// Bypass the cache if Content-Length says the entry will be too bigif(predictedDataSize!=-1&&mDiskDevice->EntryIsTooBig(predictedDataSize)){DebugOnly<nsresult>rv=nsCacheService::DoomEntry(entry);NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");returnnullptr;}entry->MarkBinding();// enter state of bindingnsresultrv=mDiskDevice->BindEntry(entry);entry->ClearBinding();// exit state of bindingif(NS_SUCCEEDED(rv))device=mDiskDevice;}}// if we can't use mDiskDevice, try mMemoryDeviceif(!device&&mEnableMemoryDevice&&entry->IsAllowedInMemory()){if(!mMemoryDevice){(void)CreateMemoryDevice();// ignore the error (check for mMemoryDevice instead)}if(mMemoryDevice){// Bypass the cache if Content-Length says entry will be too bigif(predictedDataSize!=-1&&mMemoryDevice->EntryIsTooBig(predictedDataSize)){DebugOnly<nsresult>rv=nsCacheService::DoomEntry(entry);NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");returnnullptr;}entry->MarkBinding();// enter state of bindingnsresultrv=mMemoryDevice->BindEntry(entry);entry->ClearBinding();// exit state of bindingif(NS_SUCCEEDED(rv))device=mMemoryDevice;}}if(!device&&entry->IsStreamData()&&entry->IsAllowedOffline()&&mEnableOfflineDevice){if(!mOfflineDevice){(void)CreateOfflineDevice();// ignore the error (check for mOfflineDevice instead)}device=entry->CustomCacheDevice()?entry->CustomCacheDevice():mOfflineDevice;if(device){entry->MarkBinding();nsresultrv=device->BindEntry(entry);entry->ClearBinding();if(NS_FAILED(rv))device=nullptr;}}if(device)entry->SetCacheDevice(device);returndevice;}nsresultnsCacheService::DoomEntry(nsCacheEntry*entry){returngService->DoomEntry_Internal(entry,true);}nsresultnsCacheService::DoomEntry_Internal(nsCacheEntry*entry,booldoProcessPendingRequests){if(entry->IsDoomed())returnNS_OK;CACHE_LOG_DEBUG(("Dooming entry %p\n",entry));nsresultrv=NS_OK;entry->MarkDoomed();NS_ASSERTION(!entry->IsBinding(),"Dooming entry while binding device.");nsCacheDevice*device=entry->CacheDevice();if(device)device->DoomEntry(entry);if(entry->IsActive()){// remove from active entriesmActiveEntries.RemoveEntry(entry);CACHE_LOG_DEBUG(("Removed entry %p from mActiveEntries\n",entry));entry->MarkInactive();}// put on doom list to wait for descriptors to closeNS_ASSERTION(PR_CLIST_IS_EMPTY(entry),"doomed entry still on device list");PR_APPEND_LINK(entry,&mDoomedEntries);// handle pending requests only if we're supposed toif(doProcessPendingRequests){// tell pending requests to get on with their lives...rv=ProcessPendingRequests(entry);// All requests have been removed, but there may still be open descriptorsif(entry->IsNotInUse()){DeactivateEntry(entry);// tell device to get rid of it}}returnrv;}voidnsCacheService::OnProfileShutdown(){if(!gService||!gService->mInitialized){// The cache service has been shut down, but someone is still holding// a reference to it. Ignore this call.return;}{nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN));gService->mClearingEntries=true;gService->DoomActiveEntries(nullptr);}gService->CloseAllStreams();nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_ONPROFILESHUTDOWN));gService->ClearDoomList();// Make sure to wait for any pending cache-operations before// proceeding with destructive actions (bug #620660)(void)SyncWithCacheIOThread();if(gService->mDiskDevice&&gService->mEnableDiskDevice){gService->mDiskDevice->Shutdown();}gService->mEnableDiskDevice=false;if(gService->mOfflineDevice&&gService->mEnableOfflineDevice){gService->mOfflineDevice->Shutdown();}for(autoiter=gService->mCustomOfflineDevices.Iter();!iter.Done();iter.Next()){iter.Data()->Shutdown();iter.Remove();}gService->mEnableOfflineDevice=false;if(gService->mMemoryDevice){// clear memory cachegService->mMemoryDevice->EvictEntries(nullptr);}gService->mClearingEntries=false;}voidnsCacheService::OnProfileChanged(){if(!gService)return;CACHE_LOG_DEBUG(("nsCacheService::OnProfileChanged"));nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_ONPROFILECHANGED));gService->mEnableDiskDevice=gService->mObserver->DiskCacheEnabled();gService->mEnableOfflineDevice=gService->mObserver->OfflineCacheEnabled();gService->mEnableMemoryDevice=gService->mObserver->MemoryCacheEnabled();if(gService->mDiskDevice){gService->mDiskDevice->SetCacheParentDirectory(gService->mObserver->DiskCacheParentDirectory());gService->mDiskDevice->SetCapacity(gService->mObserver->DiskCacheCapacity());// XXX initialization of mDiskDevice could be made lazily, if mEnableDiskDevice is falsensresultrv=gService->mDiskDevice->Init();if(NS_FAILED(rv)){NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing disk device failed");gService->mEnableDiskDevice=false;// XXX delete mDiskDevice?}}if(gService->mOfflineDevice){gService->mOfflineDevice->SetCacheParentDirectory(gService->mObserver->OfflineCacheParentDirectory());gService->mOfflineDevice->SetCapacity(gService->mObserver->OfflineCacheCapacity());// XXX initialization of mOfflineDevice could be made lazily, if mEnableOfflineDevice is falsensresultrv=gService->mOfflineDevice->InitWithSqlite(gService->mStorageService);if(NS_FAILED(rv)){NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing offline device failed");gService->mEnableOfflineDevice=false;// XXX delete mOfflineDevice?}}// If memoryDevice exists, reset its size to the new profileif(gService->mMemoryDevice){if(gService->mEnableMemoryDevice){// make sure that capacity is reset to the right valueint32_tcapacity=gService->mObserver->MemoryCacheCapacity();CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",capacity));gService->mMemoryDevice->SetCapacity(capacity);}else{// tell memory device to evict everythingCACHE_LOG_DEBUG(("memory device disabled\n"));gService->mMemoryDevice->SetCapacity(0);// Don't delete memory device, because some entries may be active still...}}}voidnsCacheService::SetDiskCacheEnabled(boolenabled){if(!gService)return;nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEENABLED));gService->mEnableDiskDevice=enabled;}voidnsCacheService::SetDiskCacheCapacity(int32_tcapacity){if(!gService)return;nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHECAPACITY));if(gService->mDiskDevice){gService->mDiskDevice->SetCapacity(capacity);}gService->mEnableDiskDevice=gService->mObserver->DiskCacheEnabled();}voidnsCacheService::SetDiskCacheMaxEntrySize(int32_tmaxSize){if(!gService)return;nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_SETDISKCACHEMAXENTRYSIZE));if(gService->mDiskDevice){gService->mDiskDevice->SetMaxEntrySize(maxSize);}}voidnsCacheService::SetMemoryCacheMaxEntrySize(int32_tmaxSize){if(!gService)return;nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHEMAXENTRYSIZE));if(gService->mMemoryDevice){gService->mMemoryDevice->SetMaxEntrySize(maxSize);}}voidnsCacheService::SetOfflineCacheEnabled(boolenabled){if(!gService)return;nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHEENABLED));gService->mEnableOfflineDevice=enabled;}voidnsCacheService::SetOfflineCacheCapacity(int32_tcapacity){if(!gService)return;nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_SETOFFLINECACHECAPACITY));if(gService->mOfflineDevice){gService->mOfflineDevice->SetCapacity(capacity);}gService->mEnableOfflineDevice=gService->mObserver->OfflineCacheEnabled();}voidnsCacheService::SetMemoryCache(){if(!gService)return;CACHE_LOG_DEBUG(("nsCacheService::SetMemoryCache"));nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_SETMEMORYCACHE));gService->mEnableMemoryDevice=gService->mObserver->MemoryCacheEnabled();if(gService->mEnableMemoryDevice){if(gService->mMemoryDevice){int32_tcapacity=gService->mObserver->MemoryCacheCapacity();// make sure that capacity is reset to the right valueCACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",capacity));gService->mMemoryDevice->SetCapacity(capacity);}}else{if(gService->mMemoryDevice){// tell memory device to evict everythingCACHE_LOG_DEBUG(("memory device disabled\n"));gService->mMemoryDevice->SetCapacity(0);// Don't delete memory device, because some entries may be active still...}}}/****************************************************************************** * static methods for nsCacheEntryDescriptor *****************************************************************************/voidnsCacheService::CloseDescriptor(nsCacheEntryDescriptor*descriptor){// ask entry to remove descriptornsCacheEntry*entry=descriptor->CacheEntry();booldoomEntry;boolstillActive=entry->RemoveDescriptor(descriptor,&doomEntry);if(!entry->IsValid()){gService->ProcessPendingRequests(entry);}if(doomEntry){gService->DoomEntry_Internal(entry,true);return;}if(!stillActive){gService->DeactivateEntry(entry);}}nsresultnsCacheService::GetFileForEntry(nsCacheEntry*entry,nsIFile**result){nsCacheDevice*device=gService->EnsureEntryHasDevice(entry);if(!device)returnNS_ERROR_UNEXPECTED;returndevice->GetFileForEntry(entry,result);}nsresultnsCacheService::OpenInputStreamForEntry(nsCacheEntry*entry,nsCacheAccessModemode,uint32_toffset,nsIInputStream**result){nsCacheDevice*device=gService->EnsureEntryHasDevice(entry);if(!device)returnNS_ERROR_UNEXPECTED;returndevice->OpenInputStreamForEntry(entry,mode,offset,result);}nsresultnsCacheService::OpenOutputStreamForEntry(nsCacheEntry*entry,nsCacheAccessModemode,uint32_toffset,nsIOutputStream**result){nsCacheDevice*device=gService->EnsureEntryHasDevice(entry);if(!device)returnNS_ERROR_UNEXPECTED;returndevice->OpenOutputStreamForEntry(entry,mode,offset,result);}nsresultnsCacheService::OnDataSizeChange(nsCacheEntry*entry,int32_tdeltaSize){nsCacheDevice*device=gService->EnsureEntryHasDevice(entry);if(!device)returnNS_ERROR_UNEXPECTED;returndevice->OnDataSizeChange(entry,deltaSize);}voidnsCacheService::LockAcquired(){MutexAutoLocklock(mTimeStampLock);mLockAcquiredTimeStamp=TimeStamp::Now();}voidnsCacheService::LockReleased(){MutexAutoLocklock(mTimeStampLock);mLockAcquiredTimeStamp=TimeStamp();}voidnsCacheService::Lock(){gService->mLock.Lock();gService->LockAcquired();}voidnsCacheService::Lock(mozilla::Telemetry::HistogramIDmainThreadLockerID){mozilla::Telemetry::HistogramIDlockerID;mozilla::Telemetry::HistogramIDgeneralID;if(NS_IsMainThread()){lockerID=mainThreadLockerID;generalID=mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_2;}else{lockerID=mozilla::Telemetry::HistogramCount;generalID=mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_2;}TimeStampstart(TimeStamp::Now());nsCacheService::Lock();TimeStampstop(TimeStamp::Now());// Telemetry isn't thread safe on its own, but this is OK because we're// protecting it with the cache lock. if(lockerID!=mozilla::Telemetry::HistogramCount){mozilla::Telemetry::AccumulateTimeDelta(lockerID,start,stop);}mozilla::Telemetry::AccumulateTimeDelta(generalID,start,stop);}voidnsCacheService::Unlock(){gService->mLock.AssertCurrentThreadOwns();nsTArray<nsISupports*>doomed;doomed.SwapElements(gService->mDoomedObjects);gService->LockReleased();gService->mLock.Unlock();for(uint32_ti=0;i<doomed.Length();++i)doomed[i]->Release();}voidnsCacheService::ReleaseObject_Locked(nsISupports*obj,nsIEventTarget*target){gService->mLock.AssertCurrentThreadOwns();boolisCur;if(!target||(NS_SUCCEEDED(target->IsOnCurrentThread(&isCur))&&isCur)){gService->mDoomedObjects.AppendElement(obj);}else{NS_ProxyRelease("nsCacheService::ReleaseObject_Locked::obj",target,dont_AddRef(obj));}}nsresultnsCacheService::SetCacheElement(nsCacheEntry*entry,nsISupports*element){entry->SetData(element);entry->TouchData();returnNS_OK;}nsresultnsCacheService::ValidateEntry(nsCacheEntry*entry){nsCacheDevice*device=gService->EnsureEntryHasDevice(entry);if(!device)returnNS_ERROR_UNEXPECTED;entry->MarkValid();nsresultrv=gService->ProcessPendingRequests(entry);NS_ASSERTION(rv==NS_OK,"ProcessPendingRequests failed.");// XXX what else should be done?returnrv;}int32_tnsCacheService::CacheCompressionLevel(){int32_tlevel=gService->mObserver->CacheCompressionLevel();returnlevel;}voidnsCacheService::DeactivateEntry(nsCacheEntry*entry){CACHE_LOG_DEBUG(("Deactivating entry %p\n",entry));nsresultrv=NS_OK;NS_ASSERTION(entry->IsNotInUse(),"### deactivating an entry while in use!");nsCacheDevice*device=nullptr;if(mMaxDataSize<entry->DataSize())mMaxDataSize=entry->DataSize();if(mMaxMetaSize<entry->MetaDataSize())mMaxMetaSize=entry->MetaDataSize();if(entry->IsDoomed()){// remove from Doomed listPR_REMOVE_AND_INIT_LINK(entry);}elseif(entry->IsActive()){// remove from active entriesmActiveEntries.RemoveEntry(entry);CACHE_LOG_DEBUG(("Removed deactivated entry %p from mActiveEntries\n",entry));entry->MarkInactive();// bind entry if necessary to store meta-datadevice=EnsureEntryHasDevice(entry);if(!device){CACHE_LOG_DEBUG(("DeactivateEntry: unable to bind active ""entry %p\n",entry));NS_WARNING("DeactivateEntry: unable to bind active entry\n");return;}}else{// if mInitialized == false,// then we're shutting down and this state is okay.NS_ASSERTION(!mInitialized,"DeactivateEntry: bad cache entry state.");}device=entry->CacheDevice();if(device){rv=device->DeactivateEntry(entry);if(NS_FAILED(rv)){// increment deactivate failure count++mDeactivateFailures;}}else{// increment deactivating unbound entry statistic++mDeactivatedUnboundEntries;deleteentry;// because no one else will}}nsresultnsCacheService::ProcessPendingRequests(nsCacheEntry*entry){nsresultrv=NS_OK;nsCacheRequest*request=(nsCacheRequest*)PR_LIST_HEAD(&entry->mRequestQ);nsCacheRequest*nextRequest;boolnewWriter=false;CACHE_LOG_DEBUG(("ProcessPendingRequests for %sinitialized %s %salid entry %p\n",(entry->IsInitialized()?"":"Un"),(entry->IsDoomed()?"DOOMED":""),(entry->IsValid()?"V":"Inv"),entry));if(request==&entry->mRequestQ)returnNS_OK;// no queued requestsif(!entry->IsDoomed()&&entry->IsInvalid()){// 1st descriptor closed w/o MarkValid()NS_ASSERTION(PR_CLIST_IS_EMPTY(&entry->mDescriptorQ),"shouldn't be here with open descriptors");#if DEBUG// verify no ACCESS_WRITE requests(shouldn't have any of these)while(request!=&entry->mRequestQ){NS_ASSERTION(request->AccessRequested()!=nsICache::ACCESS_WRITE,"ACCESS_WRITE request should have been given a new entry");request=(nsCacheRequest*)PR_NEXT_LINK(request);}request=(nsCacheRequest*)PR_LIST_HEAD(&entry->mRequestQ);#endif// find first request with ACCESS_READ_WRITE (if any) and promote it to 1st writerwhile(request!=&entry->mRequestQ){if(request->AccessRequested()==nsICache::ACCESS_READ_WRITE){newWriter=true;CACHE_LOG_DEBUG((" promoting request %p to 1st writer\n",request));break;}request=(nsCacheRequest*)PR_NEXT_LINK(request);}if(request==&entry->mRequestQ)// no requests asked for ACCESS_READ_WRITE, back to toprequest=(nsCacheRequest*)PR_LIST_HEAD(&entry->mRequestQ);// XXX what should we do if there are only READ requests in queue?// XXX serialize their accesses, give them only read access, but force them to check validate flag?// XXX or do readers simply presume the entry is valid// See fix for bug #467392 below}nsCacheAccessModeaccessGranted=nsICache::ACCESS_NONE;while(request!=&entry->mRequestQ){nextRequest=(nsCacheRequest*)PR_NEXT_LINK(request);CACHE_LOG_DEBUG((" %sync request %p for %p\n",(request->mListener?"As":"S"),request,entry));if(request->mListener){// Async requestPR_REMOVE_AND_INIT_LINK(request);if(entry->IsDoomed()){rv=ProcessRequest(request,false,nullptr);if(rv==NS_ERROR_CACHE_WAIT_FOR_VALIDATION)rv=NS_OK;elsedeleterequest;if(NS_FAILED(rv)){// XXX what to do?}}elseif(entry->IsValid()||newWriter){rv=entry->RequestAccess(request,&accessGranted);NS_ASSERTION(NS_SUCCEEDED(rv),"if entry is valid, RequestAccess must succeed.");// XXX if (newWriter) NS_ASSERTION( accessGranted == request->AccessRequested(), "why not?");// entry->CreateDescriptor dequeues request, and queues descriptornsICacheEntryDescriptor*descriptor=nullptr;rv=entry->CreateDescriptor(request,accessGranted,&descriptor);// post call to listener to report error or descriptorrv=NotifyListener(request,descriptor,accessGranted,rv);deleterequest;if(NS_FAILED(rv)){// XXX what to do?}}else{// read-only request to an invalid entry - need to wait for// the entry to become valid so we post an event to process// the request again later (bug #467392)nsCOMPtr<nsIRunnable>ev=newnsProcessRequestEvent(request);rv=DispatchToCacheIOThread(ev);if(NS_FAILED(rv)){deleterequest;// avoid leak}}}else{// Synchronous requestrequest->WakeUp();}if(newWriter)break;// process remaining requests after validationrequest=nextRequest;}returnNS_OK;}boolnsCacheService::IsDoomListEmpty(){nsCacheEntry*entry=(nsCacheEntry*)PR_LIST_HEAD(&mDoomedEntries);return&mDoomedEntries==entry;}voidnsCacheService::ClearDoomList(){nsCacheEntry*entry=(nsCacheEntry*)PR_LIST_HEAD(&mDoomedEntries);while(entry!=&mDoomedEntries){nsCacheEntry*next=(nsCacheEntry*)PR_NEXT_LINK(entry);entry->DetachDescriptors();DeactivateEntry(entry);entry=next;}}voidnsCacheService::DoomActiveEntries(DoomCheckFncheck){AutoTArray<nsCacheEntry*,8>array;for(autoiter=mActiveEntries.Iter();!iter.Done();iter.Next()){nsCacheEntry*entry=static_cast<nsCacheEntryHashTableEntry*>(iter.Get())->cacheEntry;if(check&&!check(entry)){continue;}array.AppendElement(entry);// entry is being removed from the active entry listentry->MarkInactive();iter.Remove();}uint32_tcount=array.Length();for(uint32_ti=0;i<count;++i){DoomEntry_Internal(array[i],true);}}voidnsCacheService::CloseAllStreams(){nsTArray<RefPtr<nsCacheEntryDescriptor::nsInputStreamWrapper>>inputs;nsTArray<RefPtr<nsCacheEntryDescriptor::nsOutputStreamWrapper>>outputs;{nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_CLOSEALLSTREAMS));nsTArray<nsCacheEntry*>entries;#if DEBUG// make sure there is no active entryfor(autoiter=mActiveEntries.Iter();!iter.Done();iter.Next()){autoentry=static_cast<nsCacheEntryHashTableEntry*>(iter.Get());entries.AppendElement(entry->cacheEntry);}NS_ASSERTION(entries.IsEmpty(),"Bad state");#endif// Get doomed entriesnsCacheEntry*entry=(nsCacheEntry*)PR_LIST_HEAD(&mDoomedEntries);while(entry!=&mDoomedEntries){nsCacheEntry*next=(nsCacheEntry*)PR_NEXT_LINK(entry);entries.AppendElement(entry);entry=next;}// Iterate through all entries and collect input and output streamsfor(size_ti=0;i<entries.Length();i++){entry=entries.ElementAt(i);nsTArray<RefPtr<nsCacheEntryDescriptor>>descs;entry->GetDescriptors(descs);for(uint32_tj=0;j<descs.Length();j++){if(descs[j]->mOutputWrapper)outputs.AppendElement(descs[j]->mOutputWrapper);for(size_tk=0;k<descs[j]->mInputWrappers.Length();k++)inputs.AppendElement(descs[j]->mInputWrappers[k]);}}}uint32_ti;for(i=0;i<inputs.Length();i++)inputs[i]->Close();for(i=0;i<outputs.Length();i++)outputs[i]->Close();}boolnsCacheService::GetClearingEntries(){AssertOwnsLock();returngService->mClearingEntries;}// staticvoidnsCacheService::GetCacheBaseDirectoty(nsIFile**result){*result=nullptr;if(!gService||!gService->mObserver)return;nsCOMPtr<nsIFile>directory=gService->mObserver->DiskCacheParentDirectory();if(!directory)return;directory->Clone(result);}// staticvoidnsCacheService::GetDiskCacheDirectory(nsIFile**result){nsCOMPtr<nsIFile>directory;GetCacheBaseDirectoty(getter_AddRefs(directory));if(!directory)return;nsresultrv=directory->AppendNative(NS_LITERAL_CSTRING("Cache"));if(NS_FAILED(rv))return;directory.forget(result);}// staticvoidnsCacheService::GetAppCacheDirectory(nsIFile**result){nsCOMPtr<nsIFile>directory;GetCacheBaseDirectoty(getter_AddRefs(directory));if(!directory)return;nsresultrv=directory->AppendNative(NS_LITERAL_CSTRING("OfflineCache"));if(NS_FAILED(rv))return;directory.forget(result);}voidnsCacheService::LogCacheStatistics(){uint32_thitPercentage=(uint32_t)((((double)mCacheHits)/((double)(mCacheHits+mCacheMisses)))*100);CACHE_LOG_INFO(("\nCache Service Statistics:\n\n"));CACHE_LOG_INFO((" TotalEntries = %d\n",mTotalEntries));CACHE_LOG_INFO((" Cache Hits = %d\n",mCacheHits));CACHE_LOG_INFO((" Cache Misses = %d\n",mCacheMisses));CACHE_LOG_INFO((" Cache Hit %% = %d%%\n",hitPercentage));CACHE_LOG_INFO((" Max Key Length = %d\n",mMaxKeyLength));CACHE_LOG_INFO((" Max Meta Size = %d\n",mMaxMetaSize));CACHE_LOG_INFO((" Max Data Size = %d\n",mMaxDataSize));CACHE_LOG_INFO(("\n"));CACHE_LOG_INFO((" Deactivate Failures = %d\n",mDeactivateFailures));CACHE_LOG_INFO((" Deactivated Unbound Entries = %d\n",mDeactivatedUnboundEntries));}nsresultnsCacheService::SetDiskSmartSize(){nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_SETDISKSMARTSIZE));if(!gService)returnNS_ERROR_NOT_AVAILABLE;returngService->SetDiskSmartSize_Locked();}nsresultnsCacheService::SetDiskSmartSize_Locked(){nsresultrv;if(mozilla::net::CacheObserver::UseNewCache()){returnNS_ERROR_NOT_AVAILABLE;}if(!mObserver->DiskCacheParentDirectory())returnNS_ERROR_NOT_AVAILABLE;if(!mDiskDevice)returnNS_ERROR_NOT_AVAILABLE;if(!mObserver->SmartSizeEnabled())returnNS_ERROR_NOT_AVAILABLE;nsAutoStringcachePath;rv=mObserver->DiskCacheParentDirectory()->GetPath(cachePath);if(NS_SUCCEEDED(rv)){nsCOMPtr<nsIRunnable>event=newnsGetSmartSizeEvent(cachePath,mDiskDevice->getCacheSize(),mObserver->ShouldUseOldMaxSmartSize());DispatchToCacheIOThread(event);}else{returnNS_ERROR_FAILURE;}returnNS_OK;}voidnsCacheService::MoveOrRemoveDiskCache(nsIFile*aOldCacheDir,nsIFile*aNewCacheDir,constchar*aCacheSubdir){boolsame;if(NS_FAILED(aOldCacheDir->Equals(aNewCacheDir,&same))||same)return;nsCOMPtr<nsIFile>aOldCacheSubdir;aOldCacheDir->Clone(getter_AddRefs(aOldCacheSubdir));nsresultrv=aOldCacheSubdir->AppendNative(nsDependentCString(aCacheSubdir));if(NS_FAILED(rv))return;boolexists;if(NS_FAILED(aOldCacheSubdir->Exists(&exists))||!exists)return;nsCOMPtr<nsIFile>aNewCacheSubdir;aNewCacheDir->Clone(getter_AddRefs(aNewCacheSubdir));rv=aNewCacheSubdir->AppendNative(nsDependentCString(aCacheSubdir));if(NS_FAILED(rv))return;nsAutoCStringnewPath;rv=aNewCacheSubdir->GetNativePath(newPath);if(NS_FAILED(rv))return;if(NS_SUCCEEDED(aNewCacheSubdir->Exists(&exists))&&!exists){// New cache directory does not exist, try to move the old one here// rename needs an empty target directory// Make sure the parent of the target sub-dir existsrv=aNewCacheDir->Create(nsIFile::DIRECTORY_TYPE,0777);if(NS_SUCCEEDED(rv)||NS_ERROR_FILE_ALREADY_EXISTS==rv){nsAutoCStringoldPath;rv=aOldCacheSubdir->GetNativePath(oldPath);if(NS_FAILED(rv))return;if(rename(oldPath.get(),newPath.get())==0)return;}}// Delay delete by 1 minute to avoid IO thrash on startup.nsDeleteDir::DeleteDir(aOldCacheSubdir,false,60000);}staticboolIsEntryPrivate(nsCacheEntry*entry){returnentry->IsPrivate();}voidnsCacheService::LeavePrivateBrowsing(){nsCacheServiceAutoLocklock;gService->DoomActiveEntries(IsEntryPrivate);if(gService->mMemoryDevice){// clear memory cachegService->mMemoryDevice->EvictPrivateEntries();}}MOZ_DEFINE_MALLOC_SIZE_OF(DiskCacheDeviceMallocSizeOf)NS_IMETHODIMPnsCacheService::CollectReports(nsIHandleReportCallback*aHandleReport,nsISupports*aData,boolaAnonymize){size_tdisk=0;if(mDiskDevice){nsCacheServiceAutoLocklock(LOCK_TELEM(NSCACHESERVICE_DISKDEVICEHEAPSIZE));disk=mDiskDevice->SizeOfIncludingThis(DiskCacheDeviceMallocSizeOf);}size_tmemory=mMemoryDevice?mMemoryDevice->TotalSize():0;MOZ_COLLECT_REPORT("explicit/network/disk-cache",KIND_HEAP,UNITS_BYTES,disk,"Memory used by the network disk cache.");MOZ_COLLECT_REPORT("explicit/network/memory-cache",KIND_HEAP,UNITS_BYTES,memory,"Memory used by the network memory cache.");returnNS_OK;}